/*
 * random_generator.cpp
 *
 *  Created on: Mar 13, 2009
 *      Author: genady
 */

#include <cstdlib>
#include <ctime>
#include <cmath>
#include <utility>
#include "random_generator.hpp"
using namespace std;

// initializes random generator
void RandomGenerator::init( unsigned int seed ) const {
	if( _seedFunc )
		_seedFunc( seed );
	else
		srand( seed );
}

// returns next random number [0, _maxRandNumber]
int RandomGenerator::nextRand() const {
	return (_randFunc) ? _randFunc() : rand();
}

RandomGenerator::RandomGenerator()
		: _randFunc(0), _seedFunc(0), _maxRandNumber(RAND_MAX) {
	_intRange = make_pair( 0, _maxRandNumber ); // default int range
	_doubleRange = make_pair( 0.0, 1.0 ); // default double range
	init( (unsigned int)time( NULL ) );
}

RandomGenerator::RandomGenerator( const pair<int, int> &range )
		: _randFunc(0), _seedFunc(0), _maxRandNumber(RAND_MAX) {
	_intRange = range.first < range.second ? // valid range?
		range : make_pair( 0, _maxRandNumber );
	_doubleRange = make_pair( 0.0, 1.0 ); // default double range
	init( (unsigned int)time( NULL ) );
}

RandomGenerator::RandomGenerator( const pair<double, double> &range )
		: _randFunc(0), _seedFunc(0), _maxRandNumber(RAND_MAX) {
	_intRange = make_pair( 0, _maxRandNumber ); // default int range
	_doubleRange = range.first < range.second ? // valid range?
		range : make_pair( 0.0, 1.0 );
	init( (unsigned int)time( NULL ) );
}

RandomGenerator::RandomGenerator( const pair<int, int> &iRange, const pair<double, double> &dRange )
		: _randFunc(0), _seedFunc(0), _maxRandNumber(RAND_MAX) {
	_intRange = iRange.first < iRange.second ? // valid range?
		iRange : make_pair( 0, _maxRandNumber );
	_doubleRange = dRange.first < dRange.second ? // valid range?
		dRange : make_pair( 0.0, 1.0 );
	init( (unsigned int)time( NULL ) );
}

RandomGenerator::RandomGenerator( const RandomGenerator &that )
		: _randFunc(that._randFunc), _seedFunc(that._seedFunc), _maxRandNumber(that._maxRandNumber),
			_intRange(that._intRange), _doubleRange(that._doubleRange) {
	init( (unsigned int)time( NULL ) );
}

RandomGenerator& RandomGenerator::operator= ( const RandomGenerator &that ) {
	if( &that != this ) {
		_randFunc = that._randFunc;
		_seedFunc = that._seedFunc;
		_maxRandNumber = that._maxRandNumber;
		_intRange = that._intRange;
		_doubleRange = that._doubleRange;
		init( (unsigned int)time( NULL ) );
	}
	return *this;
}

void RandomGenerator::setAlgorithm( RandFunc randFunc, SeedFunc seedFunc, unsigned int maxRandNumber ) {
	_randFunc = randFunc;
	_seedFunc = seedFunc;
	_maxRandNumber = maxRandNumber > 1 ? maxRandNumber : RAND_MAX;
	init( (unsigned int)time( NULL ) );
}

int RandomGenerator::nextInt() const {
	int range = _intRange.second - _intRange.first;
	int randNumber = nextRand()%(range + 1) + _intRange.first;
	return randNumber;
}

double RandomGenerator::nextDouble() const {
	double randNumber = (double)nextRand()/(double)_maxRandNumber;
	double range = _doubleRange.second - _doubleRange.first;
	randNumber = randNumber*range + _doubleRange.first;
	return randNumber;
}

NormalRandomNumber::NormalRandomNumber()
		: _nextIndex(0), _mean(0.0), _sigma(1.0) {
}

NormalRandomNumber::NormalRandomNumber( double mean, double sigma )
		: _nextIndex(0), _mean(mean), _sigma(sigma) {
}

NormalRandomNumber::NormalRandomNumber( const NormalRandomNumber &that )
		: _random(that._random), _nextIndex(that._nextIndex),
			_mean(that._mean), _sigma(that._sigma), _normalPair(that._normalPair) {
}

NormalRandomNumber& NormalRandomNumber::operator= ( const NormalRandomNumber &that ) {
	if( &that != this ) {
		_random = that._random;
		_nextIndex = that._nextIndex;
		_mean = that._mean;
		_sigma = that._sigma;
		_normalPair = that._normalPair;
	}
	return *this;
}

void NormalRandomNumber::setGenerator( const RandomGenerator &random ) {
	_random = random;
	_nextIndex = 0;
}

void NormalRandomNumber::generateNextPair() {
	double x1, x2, w, y1, y2;
	// get 2 numbers ~ N(0, 1);
	do {
		x1 = 2.0*_random.nextDouble() - 1.0;
		x2 = 2.0*_random.nextDouble() - 1.0;
		w = x1*x1 + x2*x2;
	} while( w >= 1.0 );
    w = sqrt( (-2.0*log( w ))/w );
    y1 = x1*w;
    y2 = x2*w;
    // convert using mean/sigma
    _normalPair = make_pair( _sigma*y1 + _mean, _sigma*y2 + _mean );
}

double NormalRandomNumber::next() {
	if( _nextIndex == 0 ) {
		generateNextPair();
		_nextIndex = 1;
		return _normalPair.first;
	} else {
		_nextIndex = 0;
		return _normalPair.second;
	}
}

